package main

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
	Signed | Unsigned
}

// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
	~float32 | ~float64
}

// Complex is a constraint that permits any complex numeric type.
// If future releases of Go add new predeclared complex numeric types,
// this constraint will be modified to include them.
type Complex interface {
	~complex64 | ~complex128
}

// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
	Integer | Float | ~string
}

// GenericCache 泛型缓存
type GenericCache[K Ordered, V any] struct { // 使用 Ordered
	cache          map[K]V
	cacheCleanTime map[K]int64
	updateTime     int64
	mu             sync.RWMutex
}

// checkGoVersion 检查当前 Go 版本是否支持泛型
func checkGoVersionCanUseGenerics() error {
	v := runtime.Version()
	parts := strings.Split(v, ".")
	major, err := strconv.Atoi(parts[1])
	if err != nil {
		return fmt.Errorf("failed to parse Go version: %v", err)
	}
	minor, err := strconv.Atoi(parts[2])
	if err != nil {
		return fmt.Errorf("failed to parse Go version: %v", err)
	}

	if major < 1 || (major == 1 && minor < 18) {
		return fmt.Errorf("your Go version (%s) does not support generics. Please upgrade to Go 1.18 or later.", v)
	}
	return nil
}

// NewGenericCache 创建一个新的泛型缓存
func NewGenericCache[K Ordered, V any]() (*GenericCache[K, V], error) {
	if err := checkGoVersionCanUseGenerics(); err != nil {
		return nil, err
	}
	return &GenericCache[K, V]{
		cache: make(map[K]V), cacheCleanTime: make(map[K]int64),
	}, nil
}

// Set 设置缓存项
func (c *GenericCache[K, V]) Set(key K, value V, cleanTime int64) {
	c.mu.Lock()
	defer c.mu.Unlock()
	now := time.Now()
	timestampMs := now.UnixMilli()
	if cleanTime > 0 {
		c.cacheCleanTime[key] = timestampMs + cleanTime
	} else {
		c.cacheCleanTime[key] = -1
	}
	c.updateTime = timestampMs
	c.cache[key] = value
}

// Get 获取缓存项
func (c *GenericCache[K, V]) Get(key K) (V, bool) {
	c.mu.RLock()
	var zeroValue V
	defer c.mu.RUnlock()
	if c.cacheCleanTime[key] > 0 && time.Now().UnixMilli() > c.cacheCleanTime[key] {
		return zeroValue, false
	}
	value, ok := c.cache[key]

	return value, ok
}

// ShowAllKey 获取所有缓存key
func (c *GenericCache[K, V]) ShowAllKey(isShowExpired bool) []K {
	nowUnixMilli := time.Now().UnixMilli()
	keys := make([]K, 0)
	c.mu.RLock()
	defer c.mu.RUnlock()
	for key := range c.cache {
		if !isShowExpired && c.cacheCleanTime[key] > 0 && nowUnixMilli > c.cacheCleanTime[key] {
			continue
		}
		keys = append(keys, key)
	}
	return keys
}

// Del 删除某个Key
func (c *GenericCache[K, V]) Del(key K) {
	c.mu.Lock()
	defer c.mu.Unlock()
	delete(c.cacheCleanTime, key)
	delete(c.cache, key)
}

// cleanupExpired 清理过期缓存
func (c *GenericCache[K, V]) CleanupExpired() {
	nowUnixMilli := time.Now().UnixMilli()
	keys := make([]K, 0)
	c.mu.RLock()
	for key, cleanTime := range c.cacheCleanTime {
		if cleanTime > 0 && nowUnixMilli > cleanTime {
			keys = append(keys, key)
		}
	}
	c.mu.RUnlock()
	for _, cleanKey := range keys {
		cleanNowUnixMilli := time.Now().UnixMilli()
		c.mu.Lock()
		thiscleanKeycacheCleanTime := c.cacheCleanTime[cleanKey]
		if thiscleanKeycacheCleanTime > 0 && cleanNowUnixMilli > thiscleanKeycacheCleanTime {
			delete(c.cacheCleanTime, cleanKey)
			delete(c.cache, cleanKey)
		}
		c.mu.Unlock()
	}
}

func (c *GenericCache[K, V]) UnSafeRlock() {
	c.mu.RLock()
}
func (c *GenericCache[K, V]) UnSafeRUnlock() {
	c.mu.RUnlock()
}

func (c *GenericCache[K, V]) UnSafeLock() {
	c.mu.Lock()
}
func (c *GenericCache[K, V]) UnSafeUnLock() {
	c.mu.Unlock()
}

func (c *GenericCache[K, V]) UnSafeGet(key K) (V, bool) {
	value, ok := c.cache[key]
	return value, ok
}
func (c *GenericCache[K, V]) UnSafeSet(key K, value V, cleanTime int64) {
	now := time.Now()
	timestampMs := now.UnixMilli()
	if cleanTime > 0 {
		c.cacheCleanTime[key] = timestampMs + cleanTime
	} else {
		c.cacheCleanTime[key] = -1
	}
	c.updateTime = timestampMs
	c.cache[key] = value
}

/*
func main() {
	// 获取 Unix 时间戳（毫秒）
	now := time.Now()
	timestampMs := now.UnixMilli()
	fmt.Println("Unix 时间戳（毫秒）：", timestampMs)
	stringCache, err := NewGenericCache[string, string]()
	if err != nil {
		fmt.Println(err)
		return
	}
	intCache, err := NewGenericCache[int, time.Time]()
	if err != nil {
		fmt.Println(err)
		return
	}
	stringCache.Set("name", "John Doe", 100)
	intCache.Set(1, time.Now(), -1)
	stringCache.Set("name2", "John Doe2", 888000)
	stringCache.Set("name3", "John Doe2", -1)
	fmt.Println(stringCache.ShowAllKey(false))
	stringCache.Del("name2")
	fmt.Println(stringCache.ShowAllKey(false))
	name, found := stringCache.Get("name")
	if found {
		fmt.Println("Name:", name)
	}

	timestamp, found := intCache.Get(1)
	if found {
		fmt.Println("Timestamp:", timestamp)
	}
	time.Sleep(time.Millisecond * 150)
	fmt.Println(stringCache.ShowAllKey(false))
	fmt.Println(stringCache.ShowAllKey(true))
	stringCache.CleanupExpired()
	fmt.Println(stringCache.ShowAllKey(true))

	fmt.Println(stringCache.UnSafeGet("name3"))
	stringCache.UnSafeLock()
	stringCache.UnSafeSet("name5", "John Doe5", 888000)
	fmt.Println(stringCache.UnSafeGet("name5"))
	stringCache.UnSafeUnLock()

}
*/
